package com.mipssa;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

public class Ensamblador {
	
	private Integer linea;
	private Integer direccion;
	private Vector<Linea> codigo;
	private Map<String,Integer> label; 
	private Vector<Long> opcode; /* vector que contiene las lineas con opcodes de 32 bits */
	private Map<String, Instruccion> instrucciones;
	private Procesador cpu;
	private String opcodeLinea;
	
	public Ensamblador(Vector<Linea> archParseado, Map<String, Instruccion> instr)
	{
		codigo = archParseado;
		instrucciones = instr;
		label  = new HashMap<String,Integer>();
		opcode = new Vector<Long>();
		opcodeLinea = "";
		cpu = new Procesador();
		linea = 1;
		direccion = 0;
		
	}

	public Vector<Long> ensamblar()
	{
		String instrLinea = "";
		String op = "";
		int i;
		this.eliminarLineasConComentarios(codigo);
		for(i=0; i<codigo.size();i++)
		{
			
			op = codigo.elementAt(i).getOperacion();
			
			if(op.contains(":"))
			{   /* guardar los labels con su respectiva direccion */
				this.label.put(op.substring(0,op.indexOf(":")), direccion);
				direccion--;
				//System.out.println(label.keySet().toString());
				
			}
			direccion = direccion+1;
		}
		this.eliminarLineasConLabels(codigo);
		direccion = 0;
		
		for(i=0; i<codigo.size();i++)
		{
			
			op = codigo.elementAt(i).getOperacion();
			
			if(op.contains(":"))
			{   /* guardar los labels con su respectiva direccion */
				//this.label.put(op.substring(0,op.indexOf(":")), direccion+1);
				//System.out.println(label.keySet().toString());
				
			}
			else
			{
				try {
					this.opcodeLinea = this.ensamblarLinea(codigo.elementAt(i),direccion);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					//e.printStackTrace();
					System.out.println("Error! Linea "+codigo.elementAt(i).getNumero()+": "+
							codigo.elementAt(i).getOperacion()+" "+
							codigo.elementAt(i).getParametros());
					System.exit(0);
				}
				Long num = Long.parseLong(this.opcodeLinea,2);
				opcode.addElement(num);
				String numero = String.format("%08x", num);
				//System.out.println(Integer.toHexString(num));
				//System.out.println(this.opcodeLinea);
				//System.out.println(numero);
			}
			direccion = direccion+1;
		}
		
		
		return opcode;
	}
	
	/* elimina lineas con comentarios y nulas */
	private void eliminarLineasConComentarios(Vector<Linea> lin)
	{
		int i;
		for(i=0;i<lin.size();i++)
		{
			if(lin.elementAt(i).getOperacion() == null || lin.elementAt(i).getOperacion() == "#")
			{
				lin.remove(i);
				i--;
			}
		}
	}
	
	private void eliminarLineasConLabels(Vector<Linea> lin)
	{
		int i;
		for(i=0;i<lin.size();i++)
		{
			if(lin.elementAt(i).getOperacion().contains(":"))
			{
				lin.remove(i);
				i--;
			}
		}
	}
	
	private String ensamblarLinea(Linea lin,Integer dire)
	{
		Instruccion inst = this.instrucciones.get(lin.getOperacion());
		int cantParametros = inst.getSintaxis().split(" ").length;
		String opcode = inst.getOpcode();
		String lineaCodificada = "";
		
		try {
			if(inst.getNombre().equalsIgnoreCase("NOP"))
			{
				return opcode;
			}
			
			
			{
				int i = 0;
				lineaCodificada = opcode;
				//String param1 = lin.getParametros().elementAt(0);
				//String param2 = lin.getParametros().elementAt(1);
				//String param3 = lin.getParametros().elementAt(2);
				
				
				for (i = 1 ; i < cantParametros; i++) 
				{
					if (inst.getSintaxis().split(" ")[i].contains("d")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer paramVal = this.cpu.getRegistros().get(param);
						lineaCodificada = lineaCodificada.replaceAll("ddddd",
								Util.enteroABinario(paramVal, 5));
					
						
					}
					if (inst.getSintaxis().split(" ")[i].contains("$s") && !inst.getSintaxis().split(" ")[i].contains("offset(")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer paramVal = this.cpu.getRegistros().get(param);
						lineaCodificada = lineaCodificada.replaceAll("sssss",
								Util.enteroABinario(paramVal, 5));
					}
					if (inst.getSintaxis().split(" ")[i].contains("$t")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer paramVal = this.cpu.getRegistros().get(param);
						lineaCodificada = lineaCodificada.replaceAll("ttttt",
								Util.enteroABinario(paramVal, 5));
					}
					if (inst.getSintaxis().split(" ")[i].contains("h")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer paramVal = this.cpu.getRegistros().get(param);
						lineaCodificada = lineaCodificada.replaceAll("hhhhh",
								Util.enteroABinario(paramVal, 5));
					}
					if (inst.getSintaxis().split(" ")[i].contains("imm")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer paramVal = Integer.parseInt(param);
						lineaCodificada = lineaCodificada.replaceAll("iiiiiiiiiiiiiiii",
								Util.enteroABinario(paramVal, 16));
					}
					if (inst.getSintaxis().split(" ")[i].contains("offset")) 
					{
						
						if (cantParametros == 3) 
						{						
							String param = lin.getParametros().elementAt(i - 1);
							String offset = param.substring(0,param.indexOf("("));
							String reg = param.substring(param.indexOf("(")+1,param.indexOf(")"));
							Integer paramValOffset = Integer.parseInt(offset);
							Integer paramValReg = this.cpu.getRegistros().get(reg);
							lineaCodificada = lineaCodificada.replaceAll("sssss",
									Util.enteroABinario(paramValReg, 5));
							lineaCodificada = lineaCodificada.replaceAll(
									"iiiiiiiiiiiiiiii",
									Util.enteroABinario(paramValOffset, 16));
							lineaCodificada = lineaCodificada.replaceAll("sssss",
									Util.enteroABinario(paramValReg, 5));
						}
						else
						{
							String param = lin.getParametros().elementAt(i - 1);
							Integer direcLabel = this.label.get(param);
							Integer offset = Util.getOffsetFromLabel(direcLabel, direccion);
							lineaCodificada = lineaCodificada.replaceAll(
									"iiiiiiiiiiiiiiii",
									Util.enteroABinario(offset, 16));
						}
					}
					
					if (inst.getSintaxis().split(" ")[i].contains("target")) 
					{
						String param = lin.getParametros().elementAt(i-1);
						Integer direcLabel = this.label.get(param);
						//Integer offset = Util.getOffsetFromLabel(direcLabel, direccion);
						lineaCodificada = lineaCodificada.replaceAll("iiiiiiiiiiiiiiiiiiiiiiiiii",
								Util.enteroABinario(direcLabel, 26));
					}
					
				}
				
				//System.out.println("codigo: " + lineaCodificada);
			}
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			System.out.println("Error! L"+lin.getNumero());
			//e.printStackTrace();
		}
		
		
		return lineaCodificada;
	}
}
